'use strict';
const http = require('http');
const https = require('https');
const events = require('events')
const utils = require('./util');
const util = require('util');
const urlParser = require('url');
const enums = require('./enums');

const keepAliveAgent = new http.Agent({ keepAlive: true });

const serializeManager = require('./serializeManager');


const defaultTimeout = 1000;


class Client {

    constructor() {
        let methods = ['get', 'post']
        for (let method of http.METHODS) {
            method = method.toLowerCase();
            if (methods.includes(method)) {
                continue;
            }
            this[method] = (url, query, data, options, callback) => {
                this.__request(method, url, { paramers: query, data }, options, callback);
            };
        }
    }

    /**
     * 
     * @param {*} url
     * @param {*} args {paramers, data}
     * @param {*} options {headers, timeout, noDelay}
     * @param {*} callback 
     */
    request(url, args, options, callback) {
        let method = options.method;
        args = args || {}
        options = options || {}
        this.__request(method, url, args, options, callback)
    }

    get(url, query, options, callback) {
        if (typeof query === 'function') {
            callback = query;
            query = {};
            options = {};
        }
        if (typeof options === 'function') {
            callback = options;
            query = query || {};
            options = {};
        }
        this.__request('get', url, { paramers: query }, options, callback)
    }

    post(url, data, options, callback) {
        if (typeof options === 'function') {
            callback = options;
            options = {};
        }
        this.__request('POST', url, { paramers: {}, data: data }, options, callback)
    }


    // req htt.ClientRequest
    __configureRequest(req, options, callback) {
        if (options.noDelay) {
            req.setNoDelay(true)
        }
        if (options.timeout) {
            // https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_request_settimeout_timeout_callback
            req.setTimeout(options.timeout, () => {
                req.abort();
                callback(new Error(`request expire`), null)
            })
        }
        if (options.keepAlive) {
            req.setSocketKeepAlive(options.keepAlive)
        }
    }

    __configureResponse(res, options, callback) {
        if (options.timeout) {
            res.setTimeout(options.timeout, () => {
                callback(new Error(`response expire`), null)
            })
        }
    }


    /**
     * 
     * @param {*} method 
     * @param {*} url 
     * @param {*} args {paramers, data}
     * @param {*} options 
     * @param {*} callback 
     */
    __request(method, url, args, options, callback) {
        args = args || {};
        // 只调用一次
        let cbOnce = function (err, arg) {
            callback(err, arg);
            cbOnce = function () { };
        };
        let defaultOptions = {
            timeout: defaultTimeout,
            noDelay: true,
            headers: {
                'accept-encoding': 'gzip, deflate, br',
                accept: '*/*',
            }
        }
        let urlOptions = this.__parseUrlParams(url, args.paramers || {});
        defaultOptions = Object.assign(defaultOptions, options, urlOptions);
        defaultOptions.method = method;

        // 重新计算content-length
        if (args.data !== undefined) {
            args.data = serializeManager.serializeData(args.data, defaultOptions);
            defaultOptions.headers[enums.Header.ContentLength] = Buffer.byteLength(args.data, 'utf8')
        }

        // if (args.data !== undefined && !defaultOptions.headers.hasOwnProperty(enums.Header.ContentLength)) {
        //     defaultOptions.headers[enums.Header.ContentLength] = Buffer.byteLength(args.data, 'utf8')
        // }

        // 如果设置了
        if (options.keepAlive) {
            options.agent = keepAliveAgent;
        }

        let protocol = urlOptions.protocol === 'http:' ? http : https;
        let req = protocol.request(defaultOptions, (res) => {
            this.__configureResponse(res, defaultOptions, cbOnce);
            res.on('error', err => {
                return cbOnce(err)
            })
            res.on('end', () => { // 表示所有的消息已经接收完毕
                let err = null;
                if (!res.complete) { // http 请求数据没有接收完整
                    // 暂不处理
                }
                res.data = Buffer.concat(buffer);
                return cbOnce(null, res)
            })
            let buffer = []
            res.on('data', (chunk) => {
                buffer.push(new Buffer(chunk))
            });
        });

        this.__configureRequest(req, defaultOptions, cbOnce);

        req.on('socket', (req, socket, head)=>{
            console.log('=======new connect')
        })

        req.on('error', err => {
            return cbOnce(err)
        })

        req.on('timeout', err => {
            return cbOnce(err)
        })
        if (args.data) {
            req.write(args.data)
        }
        req.end()
    }

    __parseUrlParams(url, paramers) {
        let urlInfo = urlParser.parse(url)
        if (urlInfo.host === null) {
            return {};
        }
        let query = urlInfo.query || '';
        let queryList = [];
        for (let key in paramers) {
            queryList.push(encodeURIComponent(key) + '=' + encodeURIComponent(paramers[key]));
        }
        if (queryList.length > 0) {
            query += queryList.join('&');
        }
        let path = urlInfo.pathname;
        if (query.length > 0) {
            path += '?' + query;
        }
        return {
            protocol: urlInfo.protocol,
            host: urlInfo.host,
            port: urlInfo.port || (urlInfo.protocol === 'https:' ? 443 : 80),
            path: path
        };
    }

}

util.inherits(Client, events.EventEmitter);
module.exports = { Client }